package data

import (
	"context"
	"fmt"
	"github.com/go-kratos/kratos/v2/log"
	"github.com/go-redis/redis/v8"
	"github.com/google/wire"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	gormlogger "gorm.io/gorm/logger"
	"phm/auth"
	"phm/internal/biz"
	"phm/internal/conf"
	"phm/internal/data/webmanage"
	"strconv"
	"sync"
	"time"
)

// ProviderSet is data providers.
var ProviderSet = wire.NewSet(
	NewDataRepo,
	NewGreeterRepo,
	NewRaRadarRepo,
	NewTimedTaskFaultResultRepo,
	NewSysParameterRepo,
	webmanage.NewSysUserAreaRepo,
	webmanage.NewUserRepo,
	webmanage.NewSysRoleRepo,
	webmanage.NewSysOperationRepo,
	webmanage.NewSysSysPermissionRepo,
	webmanage.NewSysMessageRepo,
)

// Data .
type dataRepo struct {
	db     *gorm.DB
	rds    *redis.Client
	auth   auth.AuthRepo
	logger *log.Helper
}

func NewDataRepo(logger log.Logger) biz.DataRepo {
	return &dataRepo{logger: log.NewHelper(logger)}
}

type dbType int

const (
	_ dbType = iota
	dbUrl
	dbPort
	dbUserName
	dbPwd
	dbName
)

var (
	//
	dbOnce   sync.Once
	authOnce sync.Once
	rdsOnce  sync.Once
)

func (d *dataRepo) GetDB() *gorm.DB {
	dbOnce.Do(func() {
		d.db = NewDatabase()
	})
	return d.db
}

func (d *dataRepo) GetRedis() *redis.Client {
	rdsOnce.Do(func() {
		d.rds = NewRedisClient()
	})
	return d.rds
}

func (d *dataRepo) GetAuth() auth.AuthRepo {
	authOnce.Do(func() {
		d.auth = auth.NewAuthRepo()
	})
	return d.auth
}

// NewDatabase .
func NewDatabase() *gorm.DB {

	db, err := gorm.Open(
		mysql.New(mysql.Config{
			DSN:               getUrl(),
			DefaultStringSize: 200,
		}),
		// 打印
		&gorm.Config{
			// gormlogger.Default.LogMode(gormlogger.Info)
			//Logger: gormlogger.New(nil, gormlogger.Config{}),
			Logger: gormlogger.Default.LogMode(gormlogger.Info),
		},
	)
	if err != nil {
		panic("数据库newDatabase发生错误")
	}

	return db
}

// NewRedisClient .
func NewRedisClient() *redis.Client {
	rdsConf := conf.Config.Data.Redis

	redisDb := redis.NewClient(&redis.Options{
		Addr:         rdsConf.Addr,
		ReadTimeout:  rdsConf.ReadTimeout.AsDuration(),
		WriteTimeout: rdsConf.WriteTimeout.AsDuration(),
		DialTimeout:  time.Second * 20,
		Password:     rdsConf.Password,
		PoolSize:     10,
	})

	timeout, cancelFunc := context.WithTimeout(context.Background(), time.Second*5)
	defer cancelFunc()
	err := redisDb.Ping(timeout).Err()
	if err != nil {
		panic(err)
	}
	return redisDb
}

func getUrl() string {
	config := conf.Config.Data
	var (
		mysqlUrl = "%v:%v@tcp(%v:%v)/%v?charset=utf8mb4&parseTime=True&loc=Local"
		dbMap    = map[dbType]string{
			dbUrl:      config.Database.Host,
			dbPort:     strconv.FormatInt(config.Database.Port, 10),
			dbUserName: config.Database.UName,
			dbPwd:      fmt.Sprintf("%v", config.Database.Pwd),
			dbName:     config.Database.DbName,
		}
	)
	return fmt.Sprintf(mysqlUrl, dbMap[dbUserName], dbMap[dbPwd], dbMap[dbUrl], dbMap[dbPort], dbMap[dbName])

}
